package com.sharing.service;

import com.alibaba.fastjson.JSON;
import com.sharing.common.Page;
import com.sharing.common.Result;
import com.sharing.domain.BrowseRecord;
import com.sharing.domain.HotData;
import com.sharing.domain.User;
import com.sharing.domain.UserProduction;
import com.sharing.mapper.BrowseRecordMapper;
import com.sharing.mapper.UserMapper;
import com.sharing.mapper.UserProductionMapper;
import com.sharing.util.StringUtils;
import com.sun.org.apache.xpath.internal.operations.Bool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.swing.text.StyledEditorKit;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author MingTian
 * @version 1.0
 * @description TODO
 * @date 2023/4/17 22:28
 */
@Service
public class HotService {
    @Autowired
    private BrowseRecordMapper browseRecordMapper;

    @Autowired
    private UserProductionMapper userProductionMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // 设置作品的上次从数据库中查询的时间
    private static Long productionBeginTime;

    // 设置用户的上次从数据库中查询的时间
    private static Long userBeginTime;

    /**
     * 查询今日最热作品（今日点击量最高的作品）的业务层方法
     * @param page 页数
     * @param size 页长度
     * @return
     */
    public Result<Page<HotData<UserProduction>>> selectHotProduction(Integer page, Integer size) {
        // 校验参数
        if (page == null || size == null) {
            return new Result<>(1, "参数不当");
        }

        List<HotData<UserProduction>> hotData = null;

        // 获取今日0时字符串
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        String datetime = dateTimeFormatter.format(localDateTime);

        // 查询今日被点击的作品总数
        Integer total = browseRecordMapper.selectHotProductionNum(datetime);

        Long length = stringRedisTemplate.opsForList().size("hot_production");
        if (length != null && length > 0) {
            // 直接从redis中查询，避免请求打到mysql服务器中
            hotData = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                String data = stringRedisTemplate.opsForList().index("hot_production", i);
                HotData hotData1 = JSON.parseObject(data, HotData.class);
                System.out.println(hotData1 + "......................");
                hotData.add(hotData1);
            }
            return new Result<>(0, new Page<>(total, size, hotData));
        }

        // 从browser_record表中查询今日所有作品的点击数，并封装成HotData
        hotData = browseRecordMapper.selectHotProduction(datetime, (page - 1) * size, size);

        // 从user_production表中获取作品信息，进一步封装HotData
        for (HotData<UserProduction> hotData1 : hotData) {
            Integer productionId = hotData1.getId();
            UserProduction userProduction = userProductionMapper.selectProductionById(productionId);
            hotData1.setData(userProduction);
        }

        // 存入redis中
        for (HotData<UserProduction> hotData1 : hotData) {
            stringRedisTemplate.opsForList().rightPush("hot_production", JSON.toJSONString(hotData1));
        }
        stringRedisTemplate.expire("hot_production", 1, TimeUnit.MINUTES);

        return new Result<>(0, new Page<>(total, size, hotData));
    }

    /**
     * 查询今日最热作品（今日点击量最高的作品）的业务层方法——————通过采取逻辑过期策略进行对缓存击穿问题针对性处理
     * @param isPro 默认为 true
     * @param page 页数
     * @param size 页长度
     * @return
     */
    public Result<Page<HotData<UserProduction>>> selectHotProduction(Boolean isPro, Integer page, Integer size) {
        // 校验参数
        if (page == null || size == null) {
            return new Result<>(1, "参数不当");
        }

        List<HotData<UserProduction>> hotData = null;

        // 获取今日0时字符串
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        String datetime = dateTimeFormatter.format(localDateTime);

        // 查询今日被点击的作品总数
        Integer total = browseRecordMapper.selectHotProductionNum(datetime);

        Long length = stringRedisTemplate.opsForList().size("hot_production");

        // 直接从redis中查询
        hotData = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            String data = stringRedisTemplate.opsForList().index("hot_production", i);
            HotData hotData1 = JSON.parseObject(data, HotData.class);
            System.out.println(hotData1 + "......................");
            hotData.add(hotData1);
        }

        // 查询上次填入redis的时间
        String beginTime = stringRedisTemplate.opsForValue().get("hot_production_begin_time");

        // 如果未到逻辑过期时间，那么直接返回redis中查询到的数据
        if (StringUtils.compareStrings(String.valueOf(System.currentTimeMillis() - 60 * 1000), beginTime)) {
            return new Result<>(0, new Page<>(total, size, hotData));
        }

        // 如果到了逻辑过期时间，需要从数据库中查询最新数据，
        // 为避免大量并发线程打入数据库，此处进行加锁控制，
        // 所有线程返回从redis中查询到的旧数据
        if (Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent("hot_production_lock", "", 10, TimeUnit.SECONDS))) {
            new Thread() {
                @Override
                public void run() {
                    // 从browser_record表中查询今日所有作品的点击数，并封装成HotData
                    List<HotData<UserProduction>> hotDatas = browseRecordMapper.selectHotProduction(datetime, (page - 1) * size, size);

                    // 从user_production表中获取作品信息，进一步封装HotData
                    for (HotData<UserProduction> hotData1 : hotDatas) {
                        Integer productionId = hotData1.getId();
                        UserProduction userProduction = userProductionMapper.selectProductionById(productionId);
                        hotData1.setData(userProduction);
                    }

                    // 存入redis中
                    for (HotData<UserProduction> hotData1 : hotDatas) {
                        stringRedisTemplate.opsForList().rightPush("hot_production", JSON.toJSONString(hotData1));
                    }

                    // 更新数据开始时间
                    stringRedisTemplate.opsForValue().set("hot_production_begin_time", System.currentTimeMillis() + "");
                }
            };
        }

        return new Result<>(0, new Page<>(total, size, hotData));
    }

    /**
     * 查询今日最热用户（今日作品被访问量最大的用户）的业务层方法
     * @param page
     * @param size
     * @return
     */
    public Result<Page<HotData<User>>> selectHotUser(Integer page, Integer size) {
        // 校验参数
        if (page == null || size == null) {
            return new Result<>(1, "参数不当");
        }

        // 获取今日0时字符串
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        String datetime = dateTimeFormatter.format(localDateTime);

        // 查询今日被访问到作品的用户总数
        Integer total = browseRecordMapper.selectHotUserNum(datetime);

        List<HotData<User>> hotData = null;

        // 从redis缓存中查询
        Long length = stringRedisTemplate.opsForList().size("hot_user");
        if (length != null && length > 0) {
            // 直接从redis中查询，避免请求打到mysql服务器中
            hotData = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                String data = stringRedisTemplate.opsForList().index("hot_user", i);
                HotData hotData1 = JSON.parseObject(data, HotData.class);
                System.out.println(hotData1 + "......................");
                hotData.add(hotData1);
            }
            return new Result<>(0, new Page<>(total, size, hotData));
        }

        // 从browser_record表中查询今日所有被访问作品的用户的点击数，并封装成HotData
        hotData = browseRecordMapper.selectHotUser(datetime, (page - 1) * size, size);

        // 从user_information表中获取用户信息，进一步封装HotData
        for (HotData<User> hotData1 : hotData) {
            Integer userId = hotData1.getId();
            User user = userMapper.selectUserById(userId);
            hotData1.setData(user);
        }

        // 存入redis中
        for (HotData<User> hotData1 : hotData) {
            stringRedisTemplate.opsForList().rightPush("hot_user", JSON.toJSONString(hotData1));
        }
        stringRedisTemplate.expire("hot_user", 1, TimeUnit.MINUTES);

        return new Result<>(0, new Page<>(total, size, hotData));
    }

    /**
     * 查询今日最热用户（今日作品被访问量最大的用户）的业务层方法——————通过采取逻辑过期策略进行对缓存击穿问题针对性处理
     * @param isPro 默认为 true
     * @param page 页数
     * @param size 页长度
     * @return
     */
    public Result<Page<HotData<User>>> selectHotUser(Boolean isPro, Integer page, Integer size) {
        // 校验参数
        if (page == null || size == null) {
            return new Result<>(1, "参数不当");
        }

        // 获取今日0时字符串
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        String datetime = dateTimeFormatter.format(localDateTime);

        // 查询今日被访问到作品的用户总数
        Integer total = browseRecordMapper.selectHotUserNum(datetime);

        List<HotData<User>> hotData = null;

        Long length = stringRedisTemplate.opsForList().size("hot_user");

        // 直接从redis中查询
        hotData = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            String data = stringRedisTemplate.opsForList().index("hot_user", i);
            HotData hotData1 = JSON.parseObject(data, HotData.class);
            System.out.println(hotData1 + "......................");
            hotData.add(hotData1);
        }

        // 查询上次填入redis的时间
        String beginTime = stringRedisTemplate.opsForValue().get("hot_user_begin_time");

        // 如果未到逻辑过期时间，那么直接返回redis中查询到的数据
        assert beginTime != null;
        if (StringUtils.compareStrings(String.valueOf(System.currentTimeMillis() - 60 * 1000), beginTime)) {
            return new Result<>(0, new Page<>(total, size, hotData));
        }

        // 如果到了逻辑过期时间，需要从数据库中查询最新数据，
        // 为避免大量并发线程打入数据库，此处进行加锁控制，
        // 所有线程返回从redis中查询到的旧数据
        if (Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent("hot_user_lock", "", 10, TimeUnit.SECONDS))) {
            new Thread() {
                @Override
                public void run() {
                    // 从browser_record表中查询今日所有作品的点击数，并封装成HotData
                    List<HotData<User>> hotDatas = browseRecordMapper.selectHotUser(datetime, (page - 1) * size, size);

                    // 从user_production表中获取作品信息，进一步封装HotData
                    for (HotData<User> hotData1 : hotDatas) {
                        Integer userId = hotData1.getId();
                        User user = userMapper.selectUserById(userId);
                        hotData1.setData(user);
                    }

                    // 存入redis中
                    for (HotData<User> hotData1 : hotDatas) {
                        stringRedisTemplate.opsForList().rightPush("hot_user", JSON.toJSONString(hotData1));
                    }

                    // 更新数据开始时间
                    stringRedisTemplate.opsForValue().set("hot_production_begin_time", System.currentTimeMillis() + "");
                }
            };
        }

        return new Result<>(0, new Page<>(total, size, hotData));
    }
}
